package com.uinnova.product.eam.service.dix.api.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.AppSystemSyncRecord;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.dix.*;
import com.uinnova.product.eam.model.dm.DataModelAttribute;
import com.uinnova.product.eam.model.dm.DataModelEntity;
import com.uinnova.product.eam.service.ICIRltSwitchSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.eam.service.dix.api.AppSystemModelSvc;
import com.uinnova.product.eam.service.dix.api.AppSystemSyncRecordSvc;
import com.uinnova.product.eam.service.es.IamsESCIDesignSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.comm.model.rlt.CcCiRlt;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.project.base.diagram.util.RedisUtil;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESAttrBean;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.dao.BaseConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用系统对接DDM平台接口
 * @author ch
 */
@Service
@Slf4j
@EnableAsync
public class AppSystemModelSvcImpl implements AppSystemModelSvc {
    @Resource
    private BmConfigSvc bmConfigSvc;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private IRltClassApiSvc rltClassApiSvc;
    @Resource
    private ICISwitchSvc iciSwitchSvc;
    @Resource
    private IamsESCIDesignSvc designCiSvc;
    @Resource
    private ICIRltSwitchSvc rltSwitchSvc;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AppSystemSyncRecordSvc systemSyncRecordSvc;
    private static final String APP_CONFIG = "{\"terminal\": \"XW-SubSystem\",\"belong\": \"所属子系统\",\"systemId\": \"子系统编号\"}";
    private static final String APP_SYSTEM_CONFIG = "APP_SYSTEM_CONFIG";
    private static final String SYSTEM = "系统级";
    private static final Integer ADD = 0;
    private static final Integer UPDATE = 1;
    @Value("${uino.eam.sync.system.open:false}")
    private boolean syncOpen;
    @Value("${uino.eam.sync.dix.url:http://10.100.52.116/dix/sync}")
    private String dixSyncUrl;
    @Override
    public List<AppSystemCategoryDTO> getAllSystemCategory() {
        AppSystemConfig appConfig = getAppConfig();
        if(CollectionUtils.isEmpty(appConfig.getRltInfo())){
            return Collections.emptyList();
        }
        Set<String> classCodes = new HashSet<>();
        Set<String> rltNames = new HashSet<>();
        for (AppSystemRltConfig each : appConfig.getRltInfo()) {
            classCodes.add(each.getSource());
            classCodes.add(each.getTarget());
            rltNames.add(each.getRlt());
        }
        List<CcCiClassInfo> ciClassList = ciClassApiSvc.getByClassCodes(classCodes, null);
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassNames(rltNames.toArray(new String[]{}));
        List<CcCiClassInfo> rltClassList = rltClassApiSvc.getRltClassByCdt(cdt);
        if(CollectionUtils.isEmpty(ciClassList) || ciClassList.size()!=classCodes.size() ||
                CollectionUtils.isEmpty(rltClassList) || rltClassList.size()!=rltNames.size()){
            log.error("调用获取全量系统目录接口:对象分类或关系分类丢失:{},{}", classCodes, rltNames);
            return Collections.emptyList();
        }
        Map<String, Long> ciClassMap = ciClassList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, CcCiClass::getId, (k1, k2) -> k2));
        Map<String, Long> rltClassMap = rltClassList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassName, CcCiClass::getId, (k1, k2) -> k2));
        ESCISearchBean bean = new ESCISearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setClassIds(Lists.newArrayList(ciClassMap.values()));
        Page<ESCIInfo> page = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        if(CollectionUtils.isEmpty(page.getData())){
            return Collections.emptyList();
        }
        Map<String, ESCIInfo> ciMap = page.getData().stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        Set<String> queryCiCodes = null;
        List<AppSystemCategoryDTO> result = new ArrayList<>();
        long start = System.currentTimeMillis();
        for (int i = 0; i < appConfig.getRltInfo().size(); i++) {
            AppSystemRltConfig each = appConfig.getRltInfo().get(i);
            List<ESCIRltInfo> rltList = queryRltByCodes(ciClassMap.get(each.getSource()), ciClassMap.get(each.getTarget()), rltClassMap.get(each.getRlt()), queryCiCodes);
            if(CollectionUtils.isEmpty(rltList)){
                return result;
            }
            queryCiCodes = createCategoryByRlt(i, rltList, ciMap, result);
            if(CollectionUtils.isEmpty(queryCiCodes)){
                return result;
            }
        }
        log.info("调用获取系统目录查询接口,共{}条目录数据", result.size());
        return result;
    }

    public List<String> getAllSystemId(AppSystemConfig appConfig) {
        List<CcCiClassInfo> ciClassList = ciClassApiSvc.getByClassCodes(Collections.singletonList(appConfig.getTerminal()), null);
        if(CollectionUtils.isEmpty(ciClassList)){
            log.info("获取系统id失败,当前系统未导入【{}】分类数据",appConfig.getTerminal());
            return Collections.emptyList();
        }
        log.info("===================子系统分类信息:{}", ciClassList.get(0).getCiClass().getClassName());
        ESCISearchBean bean = new ESCISearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setClassIds(Collections.singletonList(ciClassList.get(0).getCiClass().getId()));
        Page<ESCIInfo> page = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        if(CollectionUtils.isEmpty(page.getData())){
            log.info("获取系统id失败,当前系统未导入【{}】分类数据",appConfig.getTerminal());
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>();
        for (ESCIInfo each : page.getData()) {
            Object systemId = each.getAttrs().get(appConfig.getSystemId());
            if(systemId != null){
                result.add(systemId.toString());
            }
        }
        return result;
    }

    @Override
    public AppSystemConfig getAppConfig() {
        //做个灵活配置的口子，没有则走默认配置
        String configJson = bmConfigSvc.getConfigType(APP_SYSTEM_CONFIG);
        log.info("================数据同步配置:{}",configJson);
        if(BinaryUtils.isEmpty(configJson)){
            configJson = APP_CONFIG;
        }
        return JSON.parseObject(configJson, AppSystemConfig.class);
    }

    /**
     * 根据关系数据构建文件夹
     */
    private Set<String> createCategoryByRlt(int num, List<ESCIRltInfo> rltList, Map<String, ESCIInfo> ciMap, List<AppSystemCategoryDTO> result) {
        if(num == 0){
            List<String> sourceCiCodes = rltList.stream().map(CcCiRlt::getSourceCiCode).distinct().collect(Collectors.toList());
            for (String ciCode : sourceCiCodes) {
                ESCIInfo ciInfo = ciMap.get(ciCode);
                if(ciInfo == null){
                    continue;
                }
                String name = EamUtil.replaceSpecialStr(ciInfo.getCiLabel());
                AppSystemCategoryDTO dto = new AppSystemCategoryDTO(ciInfo.getId(), name, 0L, 1, 1);
                result.add(dto);
            }
        }
        int level = num + 2;
        Set<String> resultCodes = new HashSet<>();
        for (ESCIRltInfo each : rltList) {
            ESCIInfo sourceCiInfo = ciMap.get(each.getSourceCiCode());
            ESCIInfo targetCiInfo = ciMap.get(each.getTargetCiCode());
            if(sourceCiInfo == null || targetCiInfo == null){
                continue;
            }
            String name = EamUtil.replaceSpecialStr(targetCiInfo.getCiLabel());
            AppSystemCategoryDTO dto = new AppSystemCategoryDTO(targetCiInfo.getId(), name, sourceCiInfo.getId(), level, 1);
            result.add(dto);
            resultCodes.add(each.getTargetCiCode());
        }
        return resultCodes;
    }

    /**
     * 查询关系数据
     */
    private List<ESCIRltInfo> queryRltByCodes(Long sourceClassId, Long targetClassId, Long rltId, Set<String> sourceCiCodes){
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageSize(1);
        bean.setPageSize(5000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setSourceClassIds(Collections.singletonList(sourceClassId));
        bean.setTargetClassIds(Collections.singletonList(targetClassId));
        bean.setRltClassIds(Collections.singletonList(rltId));
        if(!BinaryUtils.isEmpty(sourceCiCodes)){
            bean.setSourceCiCodes(sourceCiCodes);
        }
        Page<ESCIRltInfo> rltInfoPage = rltSwitchSvc.searchRlt(bean, LibType.DESIGN);
        if(BinaryUtils.isEmpty(rltInfoPage.getData())){
            return Collections.emptyList();
        }
        return rltInfoPage.getData();
    }

    /**
     * 查询关系数据
     */
    private List<ESCIRltInfo> queryRltByCodes(List<Long> rltIds, Set<String> sourceCiCodes, Set<String> targetCiCodes){
        if(CollectionUtils.isEmpty(rltIds)){
            return Collections.emptyList();
        }
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageSize(1);
        bean.setPageSize(5000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setRltClassIds(rltIds);
        if(!BinaryUtils.isEmpty(sourceCiCodes)){
            bean.setSourceCiCodes(sourceCiCodes);
        }
        if(!BinaryUtils.isEmpty(targetCiCodes)){
            bean.setTargetCiCodes(targetCiCodes);
        }
        Page<ESCIRltInfo> rltInfoPage = rltSwitchSvc.searchRlt(bean, LibType.DESIGN);
        if(BinaryUtils.isEmpty(rltInfoPage.getData())){
            return Collections.emptyList();
        }
        return rltInfoPage.getData();
    }

    /**
     * 获取系统全量实体数据
     * @param entityClassId 实体分类id
     * @param systemId 系统id
     * @param belong 实体绑定系统字段：所属子系统
     */
    private List<ESCIInfo> getEntityBySystemId(Long entityClassId, String systemId, String belong){
        ESCISearchBean bean = new ESCISearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setClassIds(Collections.singletonList(entityClassId));
        ESAttrBean attrBean1 = new ESAttrBean();
        attrBean1.setKey(belong);
        attrBean1.setValue(systemId);
        attrBean1.setOptType(1);
        ESAttrBean attrBean2 = new ESAttrBean();
        attrBean2.setKey(DataModelEntity.ENTITY_TYPE);
        attrBean2.setValue(SYSTEM);
        attrBean2.setOptType(1);
        bean.setAndAttrs(Lists.newArrayList(attrBean1, attrBean2));
        Page<ESCIInfo> page = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        return BinaryUtils.isEmpty(page.getData())?Collections.emptyList():page.getData();
    }

    /**
     * 查询实体间四种关系的id
     * 一对一，一对多，多对多，父子
     */
    private Map<Long, String> queryEntityRltClassIds(){
        CCcCiClass cdt = new CCcCiClass();
        cdt.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        cdt.setClassNames(new String[]{Env.ONE_TO_ONE, Env.ONE_TO_MANY, Env.MANY_TO_MANY, Env.PATERNITY});
        List<CcCiClassInfo> classList = rltClassApiSvc.getRltClassByCdt(cdt);
        if(CollectionUtils.isEmpty(classList)){
            return Collections.emptyMap();
        }
        Map<Long, String> result = new HashMap<>();
        for (CcCiClassInfo each : classList) {
            result.put(each.getCiClass().getId(), each.getCiClass().getClassName());
        }
        return result;
    }

    /**
     * 根据ciCode查询ci信息
     */
    private List<ESCIInfo> queryCiByCiCodes(List<String> ciCodes){
        ESCISearchBean bean = new ESCISearchBean();
        bean.setCiCodes(ciCodes);
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        Page<ESCIInfo> page = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        return BinaryUtils.isEmpty(page.getData())?Collections.emptyList():page.getData();
    }

    @Override
    public AppSystemDataDTO getModel(String systemId) {
        AppSystemConfig appConfig = getAppConfig();
        //校验子系统id是否存在
        List<CcCiClassInfo> ciClassList = ciClassApiSvc.getByClassCodes(Collections.singletonList(appConfig.getTerminal()), null);
        if(CollectionUtils.isEmpty(ciClassList)){
            log.error("根据系统id获取模型接口:系统根据系统id【{}】获取系统信息失败,未创建子系统分类!", systemId);
            throw new RuntimeException("EA系统根据系统id【"+systemId+"】未获取到子系统数据!");
        }
        ESCISearchBean bean = new ESCISearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10);
        bean.setDomainId(BaseConst.DEFAULT_DOMAIN_ID);
        bean.setClassIds(Collections.singletonList(ciClassList.get(0).getCiClass().getId()));
        ESAttrBean attrBean = new ESAttrBean();
        attrBean.setKey(appConfig.getSystemId());
        attrBean.setValue(systemId);
        attrBean.setOptType(1);
        bean.setAndAttrs(Lists.newArrayList(attrBean));
        Page<ESCIInfo> page = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        if(CollectionUtils.isEmpty(page.getData())){
            log.error("根据系统id获取模型接口:系统根据系统id【{}】获取系统信息失败,未导入当前数据!", systemId);
            throw new RuntimeException("EA系统根据系统id【"+systemId+"】未获取到子系统数据!");
        }
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(BaseConst.DEFAULT_DOMAIN_ID, Env.DM_INCLUDE);
        if(rltClass == null){
            log.error("根据系统id获取模型接口:系统根据系统id【{}】获取系统信息失败,未创建实体及实体属性关系分类!", systemId);
            throw new RuntimeException("EA系统根据系统id【"+systemId+"】未获取到子系统数据!");
        }
        Map<Long, String> rltClassMap = queryEntityRltClassIds();
        Long rltClassId = rltClass.getCiClass().getId();
        List<CcCiClassInfo> classList = ciClassApiSvc.getByClassCodes(Lists.newArrayList(Env.LOGIC_ENTITY.getCode(), Env.ATTRIBUTES.getCode()), BaseConst.DEFAULT_DOMAIN_ID);
        Map<String, CcCiClass> classCodeMap = classList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, each -> each, (k1, k2) -> k2));
        //逻辑实体id
        Long entityClassId = classCodeMap.get(Env.LOGIC_ENTITY.getCode()).getId();
        //实体属性id
        Long attrClassId = classCodeMap.get(Env.ATTRIBUTES.getCode()).getId();
        return syncAppSystemDataModel(appConfig, systemId, entityClassId, attrClassId, rltClassId, rltClassMap, false);
    }
    @Async
    @Override
    public void syncAppSystemDataModel(List<ESCIInfo> releaseCiList){
        //应用子系统实体数据推送开关
        log.info("子系统数据同步开始=======================syncOpen:{},releaseCiList:{}",syncOpen, releaseCiList.size());
        if(!syncOpen || CollectionUtils.isEmpty(releaseCiList)){
            return;
        }
        List<CcCiClassInfo> classList = ciClassApiSvc.getByClassCodes(Lists.newArrayList(Env.LOGIC_ENTITY.getCode(), Env.ATTRIBUTES.getCode()), BaseConst.DEFAULT_DOMAIN_ID);
        Map<String, CcCiClass> classCodeMap = classList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, each -> each, (k1, k2) -> k2));
        //逻辑实体id
        Long entityClassId = classCodeMap.get(Env.LOGIC_ENTITY.getCode()).getId();
        //实体属性id
        Long attrClassId = classCodeMap.get(Env.ATTRIBUTES.getCode()).getId();
        AppSystemConfig appConfig = getAppConfig();
        List<String> allSystemId = getAllSystemId(appConfig);
        log.info("==================本次发布子系统id:{}=====================", allSystemId.size());
        //从发布的ci中过滤,查看是否有C'实体
        Map<String, List<ESCIInfo>> logicCiSystemMap = new HashMap<>();
        for (ESCIInfo each : releaseCiList) {
            Object type = each.getAttrs().get(DataModelEntity.ENTITY_TYPE);
            //判断是否C'实体
            if(each.getClassId().equals(entityClassId) && type!=null && SYSTEM.equals(type.toString())){
                Object belongSystem = each.getAttrs().get(appConfig.getBelong());
                if(belongSystem == null || !allSystemId.contains(belongSystem.toString())){
                    continue;
                }
                logicCiSystemMap.computeIfAbsent(belongSystem.toString(), key -> new ArrayList<>()).add(each);
            }
        }
        if(logicCiSystemMap.isEmpty()){
            log.info("================本次发布无系统级逻辑实体,数据同步结束=====================");
            return;
        }
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(BaseConst.DEFAULT_DOMAIN_ID, Env.DM_INCLUDE);
        if(rltClass == null){
            log.info("================数据推送失败,关系分类{}不存在!", Env.DM_INCLUDE);
            return;
        }
        Map<Long, String> rltClassMap = queryEntityRltClassIds();
        log.info("================实体关系分类:{}=====================", rltClassMap.keySet());
        Long rltClassId = rltClass.getCiClass().getId();
        for (String systemId : logicCiSystemMap.keySet()) {
            syncAppSystemDataModel(appConfig, systemId, entityClassId, attrClassId, rltClassId, rltClassMap, true);
        }
    }

    /**
     * 组装推送数据格式
     * @param appConfig 配置信息
     * @param systemId 系统id
     * @param entityClassId 实体分类id
     * @param attrClassId 实体属性id
     * @param rltClassId 实体与实体属性关系分类id
     * @param rltClassMap 实体间四种关系分类
     * @param sync 是否同步
     */
    private AppSystemDataDTO syncAppSystemDataModel(AppSystemConfig appConfig, String systemId, Long entityClassId, Long attrClassId,
                                        Long rltClassId, Map<Long, String> rltClassMap, boolean sync) {
        //===========处理实体及实体属性===============
        //获取当前资产库系统所有实体及关系数据
        log.info("================按系统同步实体数据开始,子系统编号:{}=====================", systemId);
        List<ESCIInfo> allEntity = getEntityBySystemId(entityClassId, systemId, appConfig.getBelong());
        AppSystemSyncRecord maxVersionRecord = systemSyncRecordSvc.getMaxVersionRecordBySystemId(systemId);
        //获取所有实体属性
        Set<String> entityIds = allEntity.stream().map(CcCi::getCiCode).collect(Collectors.toSet());
        log.info("================子系统{},实体{}=====================", systemId, entityIds);
        List<ESCIRltInfo> allEntityAttrRlt = queryRltByCodes(entityClassId, attrClassId, rltClassId, entityIds);
        log.info("================子系统{},实体属性关系条数:{}=====================", systemId, allEntityAttrRlt.size());
        Map<String, List<ESCIRltInfo>> allEntityAttrRltMap = new HashMap<>();
        List<String> attrCiCodes = new ArrayList<>();
        for (ESCIRltInfo each : allEntityAttrRlt) {
            attrCiCodes.add(each.getTargetCiCode());
            allEntityAttrRltMap.computeIfAbsent(each.getSourceCiCode(), key->new ArrayList<>()).add(each);
        }
        List<ESCIInfo> attrCiList = queryCiByCiCodes(attrCiCodes);
        Map<String, ESCIInfo> attrCiMap = attrCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each));
        AppSystemDataDTO result = new AppSystemDataDTO(systemId);
        List<AppSystemEntityVO> entityVoList = new ArrayList<>();
        Map<String, List<String>> entityAttr = new HashMap<>();
        int version = 1;
        List<String> rltIds = new ArrayList<>();
        if(!BinaryUtils.isEmpty(maxVersionRecord)){
            entityAttr = maxVersionRecord.getEntityAttr();
            version = maxVersionRecord.getVersion()+1;
            rltIds = maxVersionRecord.getRltIds();
        }
        log.info("================子系统{}最大版本:{}=====================", systemId, version);
        Set<String> lastVersionEntity = entityAttr.keySet();
        Set<String> currentEntityIds = new HashSet<>();
        //用作记录新一次推送记录
        Map<String, List<String>> newEntityAttr = new HashMap<>();
        //本次发布的实体(针对上面全量数据，有的是更新，没有的是新增)
        for (ESCIInfo entity : allEntity) {
            currentEntityIds.add(entity.getCiCode());
            String cnName = entity.getAttrs().getOrDefault(DataModelEntity.NAME_CN, "未命名").toString();
            String enName = entity.getAttrs().getOrDefault(DataModelEntity.NAME_EN, "NameLess").toString();
            //查询上次发布记录,如果有,则是更新,如果没有,则是新增
            Integer status = lastVersionEntity.contains(entity.getCiCode())?UPDATE:ADD;
            AppSystemEntityVO entityVO = new AppSystemEntityVO(entity.getCiCode(), cnName, enName, status);
            List<ESCIRltInfo> allAttrRlt = allEntityAttrRltMap.getOrDefault(entity.getCiCode(), new ArrayList<>());
            List<String> lastVersionAttr = entityAttr.getOrDefault(entity.getCiCode(), new ArrayList<>());
            Set<String> currentAttrIds = getAttrInfo(allAttrRlt, attrCiMap, lastVersionAttr, entityVO);
            entityVoList.add(entityVO);
            newEntityAttr.put(entity.getCiCode(), new ArrayList<>(currentAttrIds));
        }
        //根据最新一次发布记录，将上一次所有实体code集合removeAll当前的,剩余的放到delList
        lastVersionEntity.removeAll(currentEntityIds);
        result.setVersion(version);
        result.setDelEntityIds(new ArrayList<>(lastVersionEntity));
        result.setEntityList(entityVoList);
        //=================处理关系===============
        Set<String> currentRltIds = getRltInfo(rltClassMap, entityIds, rltIds, allEntityAttrRltMap, attrCiMap, result);
        if(sync){
            //===============数据推送=================
            log.info("================数据推送开始=====================");
            int sendStatus = sendToDIX(result, systemId);
            //===========记录数据推送日志===============
            AppSystemSyncRecord record = new AppSystemSyncRecord();
            record.setSystemId(systemId);
            record.setStatus(sendStatus);
            record.setVersion(version);
            record.setEntityAttr(newEntityAttr);
            record.setRltIds(new ArrayList<>(currentRltIds));
            systemSyncRecordSvc.saveOrUpdate(record);
            log.info("================数据推送结束=====================");
        }
        return result;
    }

    /**
     * 推送数据到dix
     */
    private int sendToDIX(AppSystemDataDTO result, String systemId) {
        HttpRequest request = HttpRequest.post(dixSyncUrl).cookie().body(JSON.toJSONString(result)).timeout(20000);
        log.info("应用子系统:数据推送地址{}", dixSyncUrl);
        int sendStatus = 1;
        try (HttpResponse response = request.execute()) {
            if (!response.isOk()) {
                sendStatus = 0;
                log.info("应用子系统:{}模型数据推送失败!", systemId);
            }else{
                log.info("应用子系统:{}模型数据推送成功!", systemId);
            }
        }
        return sendStatus;
    }

    /**
     * 组装实体属性返回信息
     * @param rltList <实体-包含-实体属性>关系集合
     * @param attrCiMap 实体属性ci集合
     * @param lastVersionAttr 上一次发布版本的实体属性ciCode集合
     * @param entity 实体
     */
    private Set<String> getAttrInfo(List<ESCIRltInfo> rltList, Map<String, ESCIInfo> attrCiMap, List<String> lastVersionAttr, AppSystemEntityVO entity){
        List<AppSystemAttributeVO> attributeList = new ArrayList<>();
        Set<String> currentAttrIds = new HashSet<>();
        for (ESCIRltInfo each : rltList) {
            ESCIInfo entityAttr = attrCiMap.get(each.getTargetCiCode());
            if(entityAttr == null){
                continue;
            }
            currentAttrIds.add(entityAttr.getCiCode());
            Map<String, Object> attrs = entityAttr.getAttrs();
            String cnName = attrs.computeIfAbsent(DataModelAttribute.NAME_CN, key->"未命名").toString();
            String enName = attrs.computeIfAbsent(DataModelAttribute.NAME_EN, key->"NameLess").toString();
            String dataType = attrs.computeIfAbsent(DataModelAttribute.DATA_TYPE, key->"").toString();
            String length = attrs.computeIfAbsent(DataModelAttribute.LENGTH, key->"").toString();
            String precision = attrs.computeIfAbsent(DataModelAttribute.PRECISION, key->"").toString();
            StringBuilder intactDataType = new StringBuilder(dataType);
            if(!length.isEmpty()){
                intactDataType.append("(").append(length).append(precision.isEmpty()?"":(","+precision)).append(")");
            }
            String nonEmpty = attrs.computeIfAbsent(DataModelAttribute.NON_EMPTY, key->"false").toString();
            String primaryKey = attrs.computeIfAbsent(DataModelAttribute.PRIMARY_KEY, key->"false").toString();
            String foreignKey = attrs.computeIfAbsent(DataModelAttribute.FOREIGN_KEY, key->"false").toString();
            AppSystemAttributeVO attribute = new AppSystemAttributeVO(entityAttr.getCiCode(), cnName, enName);
            attribute.setDataType(intactDataType.toString());
            attribute.setNonEmpty(Boolean.parseBoolean(nonEmpty));
            attribute.setPrimaryKey(Boolean.parseBoolean(primaryKey));
            attribute.setForeignKey(Boolean.parseBoolean(foreignKey));
            Integer status = lastVersionAttr.contains(entityAttr.getCiCode())?UPDATE:ADD;
            attribute.setStatus(status);
            attributeList.add(attribute);
        }
        entity.setAttributes(attributeList);
        //根据上次发布记录，根据所有实体属性code集合removeAll当前的,剩余的放到delAttrList
        lastVersionAttr.removeAll(currentAttrIds);
        entity.setDelAttributeIds(lastVersionAttr);
        return currentAttrIds;
    }

    /**
     * 组装实体间关系
     */
    private Set<String> getRltInfo(Map<Long, String> rltClassMap, Set<String> entityIds, List<String> recordRltIds,
                                   Map<String, List<ESCIRltInfo>> entityAttrRltMap, Map<String, ESCIInfo> attrCiMap, AppSystemDataDTO result){
        List<ESCIRltInfo> entityInlineRlt = queryRltByCodes(new ArrayList<>(rltClassMap.keySet()), entityIds, entityIds);
        List<AppSystemRltVO> rltList = new ArrayList<>();
        Set<String> currentRltIds = new HashSet<>();
        for (ESCIRltInfo each : entityInlineRlt) {
            String rltId = each.getId().toString();
            currentRltIds.add(rltId);
            Integer status = recordRltIds.contains(rltId)?UPDATE:ADD;
            String className = rltClassMap.get(each.getClassId());
            String rltType = getRltType(className);
            AppSystemRltVO rltVO = new AppSystemRltVO(rltId, className, rltType, each.getSourceCiCode(), each.getTargetCiCode(), status);
            //键映射
            List<Map<String, String>> listKeyMap = getListKeyMap(each.getSourceCiCode(), each.getTargetCiCode(), entityAttrRltMap, attrCiMap);
            rltVO.setKeyMap(listKeyMap);
            rltList.add(rltVO);
        }
        recordRltIds.removeAll(currentRltIds);
        result.setRltList(rltList);
        result.setDelRltIds(recordRltIds);
        return currentRltIds;
    }

    private List<Map<String, String>> getListKeyMap(String sourceCiCode, String targetCiCode, Map<String, List<ESCIRltInfo>> entityAttrRltMap, Map<String, ESCIInfo> attrCiMap) {
        List<Map<String, String>> result = new ArrayList<>();
        List<ESCIRltInfo> sourceRlt = entityAttrRltMap.get(sourceCiCode);
        List<ESCIRltInfo> targetRlt = entityAttrRltMap.get(targetCiCode);
        if(CollectionUtils.isEmpty(sourceRlt) || CollectionUtils.isEmpty(targetRlt)){
            return result;
        }
        List<String> sourceAttrs = sourceRlt.stream().map(CcCiRlt::getTargetCiCode).collect(Collectors.toList());
        List<String> targetAttrs = targetRlt.stream().map(CcCiRlt::getTargetCiCode).collect(Collectors.toList());
        //inheritId继承标识-实体属性code
        Map<String, String> primaryMap = new HashMap<>();
        for (String each : sourceAttrs) {
            ESCIInfo ci = attrCiMap.get(each);
            Object primaryKeyObj = ci.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
            if(primaryKeyObj == null){
                continue;
            }
            boolean primaryKey = Boolean.parseBoolean(primaryKeyObj.toString());
            if(primaryKey){
                String inheritId = ci.getAttrs().get(DataModelAttribute.INHERIT_ID).toString();
                primaryMap.put(inheritId, each);
            }
        }
        if(primaryMap.isEmpty()){
            return result;
        }
        //inheritId继承标识-实体属性code
        Map<String, String> foreignMap = new HashMap<>();
        for (String each : targetAttrs) {
            ESCIInfo ci = attrCiMap.get(each);
            Object foreignKeyObj = ci.getAttrs().get(DataModelAttribute.FOREIGN_KEY);
            if(foreignKeyObj == null){
                continue;
            }
            boolean foreignKey = Boolean.parseBoolean(foreignKeyObj.toString());
            if(foreignKey){
                String inheritId = ci.getAttrs().get(DataModelAttribute.INHERIT_ID).toString();
                foreignMap.put(inheritId, each);
            }
        }
        for (Map.Entry<String, String> each : primaryMap.entrySet()) {
            String foreignCode = foreignMap.get(each.getKey());
            if(foreignCode == null){
                continue;
            }
            Map<String, String> map = new HashMap<>();
            map.put("primaryId", each.getValue());
            map.put("foreignId", foreignCode);
            result.add(map);
        }
        return result;
    }

    /**
     * 更据关系名获取关系类型
     */
    private String getRltType(String rltName){
        switch (rltName){
            case Env.ONE_TO_ONE:
                return "ONE_TO_ONE";
            case Env.ONE_TO_MANY:
                return "ONE_TO_MANY";
            case Env.MANY_TO_MANY:
                return "MANY_TO_MANY";
            case Env.PATERNITY:
                return "PATERNITY";
            default:
                return "NameLess";
        }
    }

}
